package org.example.service;

import lombok.Data;
import lombok.SneakyThrows;
import org.example.model.LdapUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ldap.core.AuthenticationSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextMapper;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.naming.Name;
import javax.naming.NameNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: LiFuqiang
 * @Date: 2020/9/27 13:43
 * @Version 1.0
 */
@Data
@Service
public class LdapService implements UserDetailsService {

    @Value("${ldap.url}")
    private String ldapUrl;

    @Value("${ldap.base}")
    private String ldapBase;

    @Value("${ldap.group.base}")
    private String ldapGroupBase;

    @Override
    public LdapUser loadUserByUsername(String username) throws UsernameNotFoundException {
        LdapUser user = loadUser(username);
       if(user != null){
           return user;
       }
       throw new UsernameNotFoundException("user name not found");
    }

    /**
     * 获取 ou=people的 ldaptemplate
     *
     * @return
     */
    public LdapTemplate getPeopleAdminLdapTemplate() {
        LdapTemplate adminLdapTemplate = new LdapTemplate();

        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setCacheEnvironmentProperties(false);
        contextSource.setUrl(ldapUrl);
        contextSource.setBase(ldapBase);
        contextSource.setAuthenticationSource(new AuthenticationSource() {
            @Override
            public String getPrincipal() {
                return "cn=Manager,dc=virtueit,dc=net";
            }

            @Override
            public String getCredentials() {
                return "Jr2hXQ1jXpEJ4H2tO7cj";
            }
        });
        adminLdapTemplate.setContextSource(contextSource);

        return adminLdapTemplate;
    }

    /**
     * 获取 ou=Groups的LdapTemplate
     *
     * @return
     */
    public LdapTemplate getGroupAdminLdapTemplate() {
        LdapTemplate adminLdapTemplate = new LdapTemplate();

        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setCacheEnvironmentProperties(false);
        contextSource.setUrl(ldapUrl);
        contextSource.setBase(ldapGroupBase);
        contextSource.setAuthenticationSource(new AuthenticationSource() {
            @Override
            public String getPrincipal() {
                return "cn=Manager,dc=virtueit,dc=net";
            }

            @Override
            public String getCredentials() {
                return "Jr2hXQ1jXpEJ4H2tO7cj";
            }
        });
        adminLdapTemplate.setContextSource(contextSource);

        return adminLdapTemplate;
    }

    public LdapUser loadUser(String username)  {
        LdapTemplate ldapTemplate = getPeopleAdminLdapTemplate();
        try {

            DirContextAdapter obj = (DirContextAdapter) ldapTemplate.lookup("uid=" + username);

            byte[] password = (byte[]) obj.getObjectAttribute("userpassword");
            String dn = obj.getDn().toString();
            String base = obj.getNameInNamespace();
            System.out.println();

            LdapUser user = new LdapUser();
            user.setUsername(username);
            user.setDn(dn);
            user.setPassword(new String(password, "UTF-8"));
            user.setRoles(listRoles(username));
            return user;
        }catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }catch (Exception e){
            if(e instanceof NameNotFoundException){
                throw new UsernameNotFoundException(e.getMessage(),e);
            }
        }
        return null;
    }

    public List<String> listRoles(String username) {
        return listGroups(username);
    }

    /**
     * 显示所有的group
     *
     * @param name
     * @return
     */
    public List<String> listGroups(String name) {

        LdapTemplate ldapTemplate = getGroupAdminLdapTemplate();

        String baseDn = "dc=virtueit,dc=net";
        String filter = "(memberUid=" + name + ")";

        EqualsFilter f = new EqualsFilter("memberUid", name);

        AbstractContextMapper<Object> map = new AbstractContextMapper<Object>() {
            @SneakyThrows
            @Override
            protected Object doMapFromContext(DirContextOperations ctx) {
                return ctx.getDn();
            }
        };

        List<Object> result2 = ldapTemplate.search("", f.toString(), map);
        List<String> result = result2.stream().map(x -> x.toString().split("=")[1]).collect(Collectors.toList());

        return result;
    }

    /**
     * 验证用户登录
     *
     * @param userName
     * @param password
     * @return
     */
    public boolean authenticate(String userName, String password) {
        LdapTemplate ldapTemplate = new LdapTemplate();

        LdapContextSource contextSource = new LdapContextSource();
        contextSource.setUrl(ldapUrl);
        contextSource.setUserDn("uid=" + userName + "," + ldapBase);
        contextSource.setPassword(password);
        contextSource.setPooled(false);
        contextSource.afterPropertiesSet(); // important

        ldapTemplate.setContextSource(contextSource);
        try {
            ldapTemplate.authenticate("uid=" + userName + ",ou=People,dc=virtueit,dc=net", "(objectClass=person)", password);
            return true;
        } catch (Exception e) {
           throw e;
        }
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        LdapService ldapService = new LdapService();

        ldapService.setLdapBase("ou=People,dc=virtueit,dc=net");
        ldapService.setLdapUrl("ldap://10.61.60.24:389");
        ldapService.setLdapGroupBase("ou=Groups,dc=virtueit,dc=net");

        ldapService.loadUser("concar9");
    }
}
